package com.frontop.terminal.util;


import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 文件操作工具类
 *
 * @author Doumou
 * @date 2020/8/27
 */
@Slf4j
public class FileUtils extends FileUtil {

    /**
     * 根据系统类型获取分割符
     */
    public static final char FILE_SEPARATOR = FileUtil.isWindows() ? CharUtil.BACKSLASH : CharUtil.SLASH;
    public static final Sign SIGN;
    /**
     * 允许上传的图片类型
     */
    private static final Map<String, String> imgTypeMap;
    /**
     * 允许上传的音频类型
     */
    private static final Map<String, String> audioTypeMap;
    /**
     * 允许上传的音频类型
     */
    private static final Map<String, String> videoTypeMap;
    /**
     * 允许上传的文件类型
     */
    private static final Map<String, String> permitTypeMap;
    private static final String MD5PRIVATEKEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJHIO7ZgRZr0tEfUlYietXE4tnLTQ5b4LJbqY0L+TaxPoLgVcF+GhDu49Fx9d6LPA5JiZtn2yy4CvIEjyxQ/t9B7Pd9VfCcOXdsT1Cn5GFDRYdEPTgBEAdc+sngKp7PAJeB5CuFJK94G92qzHWPEYrtsKHnMWRdveome6sy2IXFLAgMBAAECgYBdbZcfNanGOkeDtXE0AorEChWbl1u82EYzXfnJiJMc+FdHQq4m+l2qrMkmsV5H4CYo3ZmO3QSv8x7+IINY8dEJ+i8U5UqBQ7cUUw+r5gjFx4a2I5ezQnrpXwIv3DAeHItDGr3RsWC6ZEIYl4063ZJVJFDlgB/05D2m0iJdGDvCmQJBANLq0tcI10o/gOe5JrePYesU1AZvfH5TcGN1h5Tav1KB/04VV4sU7hlXCGiNr+A/xvrre69gqHnUQGAes1+C/j8CQQCw8Uh0iAHTqLE71kQzdQ1ZT+j2LsBeVoFe27FhN7Vh+MxtGoIcO1nII4IzgsKhW4ThQO5hsJi40TUXEwzxgiH1AkBgAucxXePq95dtZHo8aveu9I9D0rJAKylpDRJH73/SRjz6xXpRZJiyTk0UkxLxqPaiOFnYTs7Cjp0zZCZRHycvAkAsZwAlrhO3R7fiKizHNPJ6nSD61lST1+VzJnJm0RIFWegC4QoRh5tKLA76Wi/5SfwYgrDPdv98MZO/DJrM6DkZAkEAg7ruXt/fV+5/8IXJ5AmwOVIwEZ9+VTxR/tN/nN9pW/OW20N0wR/IkW6SME0++8kVV+wG4MJdUI9+LX4r+p0YZA";
    private static final String MD5PUBLICKEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCRyDu2YEWa9LRH1JWInrVxOLZy00OW+CyW6mNC/k2sT6C4FXBfhoQ7uPRcfXeizwOSYmbZ9ssuAryBI8sUP7fQez3fVXwnDl3bE9Qp+RhQ0WHRD04ARAHXPrJ4CqezwCXgeQrhSSveBvdqsx1jxGK7bCh5zFkXb3qJnurMtiFxSwIDAQAB";

    static {
        SIGN = SecureUtil.sign(SignAlgorithm.MD5withRSA, MD5PRIVATEKEY, MD5PUBLICKEY);

        imgTypeMap = new ConcurrentHashMap<>();
        imgTypeMap.put("ffd8ff", "jpg");
        // PNG (png)
        imgTypeMap.put("89504e47", "png");
        // GIF (gif)
        imgTypeMap.put("4749463837", "gif");
        // GIF (gif)
        imgTypeMap.put("4749463839", "gif");

        audioTypeMap = new ConcurrentHashMap<>();
        audioTypeMap.put("49443303000000002176", "mp3");
        // Wave (wav)
        audioTypeMap.put("52494646e27807005741", "wav");
        audioTypeMap.put("unknown15", "aac");

        videoTypeMap = new ConcurrentHashMap<>();
        videoTypeMap.put("32454DX4s5", "mp4");
        videoTypeMap.put("asd35xxojdw", "wmv");
        videoTypeMap.put("asdewqxjdw", "rmvb");
        videoTypeMap.put("23x5ewqxjdw", "rm");
        videoTypeMap.put("543xjd67v", "mpeg");
        videoTypeMap.put("32454savbn5", "avi");
        videoTypeMap.put("4325254DX4s5", "mpg");
        videoTypeMap.put("324xejxx77", "mkv");
        videoTypeMap.put("3wl2x454D4s5", "m2ts");
        videoTypeMap.put("2454DX4sxx", "webm");
        videoTypeMap.put("3954D5X4s5", "flv");
        videoTypeMap.put("82454DXs5", "asf");
        videoTypeMap.put("xs54DX4s55", "mov");
        videoTypeMap.put("33454Ds4s5", "m4v");
        videoTypeMap.put("3x4545cDs5", "rm");
        videoTypeMap.put("32254Dxs5", "vob");
        videoTypeMap.put("3294DX64s5", "ogv");
        videoTypeMap.put("324541gX4s5", "swf");
        videoTypeMap.put("sda2321s5a", "gif");

        permitTypeMap = new ConcurrentHashMap<>();
        // JPEG (jpg)
        permitTypeMap.put("ffd8ff", "jpg");
        // PNG (png)
        permitTypeMap.put("89504e47", "png");
        // GIF (gif)
        permitTypeMap.put("4749463837", "gif");
        // GIF (gif)
        permitTypeMap.put("4749463839", "gif");
        // TIFF (tif)
        permitTypeMap.put("49492a00227105008037", "tif");
        // 16色位图(bmp)
        permitTypeMap.put("424d228c010000000000", "bmp");
        // 24位位图(bmp)
        permitTypeMap.put("424d8240090000000000", "bmp");
        // 256色位图(bmp)
        permitTypeMap.put("424d8e1b030000000000", "bmp");
        // CAD (dwg)
        permitTypeMap.put("41433130313500000000", "dwg");
        // Rich Text Format (rtf)
        permitTypeMap.put("7b5c727466315c616e73", "rtf");
        // Photoshop (psd)
        permitTypeMap.put("38425053000100000000", "psd");
        // Email [Outlook Express 6] (eml)
        permitTypeMap.put("46726f6d3a203d3f6762", "eml");
        // MS Access (mdb)
        permitTypeMap.put("5374616E64617264204A", "mdb");
        permitTypeMap.put("252150532D41646F6265", "ps");
        // Adobe Acrobat (pdf)
        permitTypeMap.put("255044462d312e", "pdf");
//        permitTypeMap.put("2e524d46000000120001", "rmvb"); // rmvb/rm相同
//        permitTypeMap.put("464c5601050000000900", "flv"); // flv与f4v相同
        permitTypeMap.put("00000020667479706", "mp4");
        permitTypeMap.put("00000018667479706D70", "mp4");
        permitTypeMap.put("49443303000000002176", "mp3");
        permitTypeMap.put("000001ba210001000180", "mpg");
        // wmv与asf相同
        permitTypeMap.put("3026b2758e66cf11a6d9", "wmv");
        // Wave (wav)
        permitTypeMap.put("52494646e27807005741", "wav");
        // MIDI (mid)
        permitTypeMap.put("52494646d07d60074156", "avi");
        permitTypeMap.put("4d546864000000060001", "mid");
        // WinRAR
        permitTypeMap.put("526172211a0700cf9073", "rar");
//        permitTypeMap.put("235468697320636f6e66", "ini");
        permitTypeMap.put("504B03040a0000000000", "jar");
        permitTypeMap.put("504B0304140008000800", "jar");
        permitTypeMap.put("504B0304340008000800", "zip");
        permitTypeMap.put("504B03043w0008000800", "7z");
        permitTypeMap.put("4d5a9000030000000400", "exe");
        // jsp文件
        permitTypeMap.put("3c25402070616765206c", "jsp");
        // MF文件
        permitTypeMap.put("4d616e69666573742d56", "mf");
        // java文件
        permitTypeMap.put("7061636b616765207765", "java");
        // bat文件
        permitTypeMap.put("406563686f206f66660d", "bat");
        // gz文件
        permitTypeMap.put("1f8b0800000000000000", "gz");
        // bat文件
        permitTypeMap.put("cafebabe0000002e0041", "class");
        // bat文件
        permitTypeMap.put("49545346030000006000", "chm");
        // bat文件
        permitTypeMap.put("04000000010000001300", "mxp");
        permitTypeMap.put("6431303a637265617465", "torrent");
        // Quicktime (mov)
        permitTypeMap.put("6D6F6F76", "mov");
        // WordPerfect (wpd)
        permitTypeMap.put("FF575043", "wpd");
        // Outlook Express (dbx)
        permitTypeMap.put("CFAD12FEC5FD746F", "dbx");
        // Outlook (pst)
        permitTypeMap.put("2142444E", "pst");
        // Quicken (qdf)
        permitTypeMap.put("AC9EBD8F", "qdf");
        // Windows Password (pwl)
        permitTypeMap.put("E3828596", "pwl");
        // Real Audio (ram)
        permitTypeMap.put("2E7261FD", "ram");
        // MS Excel 注意：word、msi 和 excel的文件头一样
        permitTypeMap.put("d0cf11e0a1b11ae10", "xls");
        // xlsx,docx,zip都是同一种文件头，xlsx,docx是xls、doc的压缩:以下文件的文件头不定,暂用文件扩展名确定
        permitTypeMap.put("unknown1", "xlsx");
        permitTypeMap.put("unknown2", "docx");
        permitTypeMap.put("unknown3", "ppt");
        permitTypeMap.put("unknown4", "pptx");
        permitTypeMap.put("unknown5", "war");
        permitTypeMap.put("unknown6", "msi");
        permitTypeMap.put("unknown7", "doc");
        permitTypeMap.put("unknown8", "txt");

        //未知文件头的音频格式
        permitTypeMap.put("unknown9", "cda");
        permitTypeMap.put("unknown10", "wma");
        permitTypeMap.put("unknown11", "ra");
        permitTypeMap.put("unknown12", "midi");
        permitTypeMap.put("unknown13", "ogg");
        permitTypeMap.put("unknown14", "flac");
        permitTypeMap.put("unknown15", "aac");
        permitTypeMap.put("unknown16", "ape");

    }

    /**
     * 判断文件是否为图片类型:暂时支持map中的类型判断
     *
     * @param fileType 文件类型
     * @return boolean
     */
    public static boolean isImg(String fileType) {
        if (fileType == null) {
            return false;
        }
        return imgTypeMap.containsValue(fileType.toLowerCase());
    }

    /**
     * 判断文件是否为音频类型:暂时支持map中的类型判断
     *
     * @param fileType 文件类型
     * @return boolean
     */
    public static boolean isAudio(String fileType) {
        if (fileType == null) {
            return false;
        }
        return audioTypeMap.containsValue(fileType);
    }

    /**
     * 判断文件是否为视频类型，成立为true
     *
     * @param fileType 文件类型
     * @return boolean
     */
    public static boolean isVideo(String fileType) {
        if (fileType == null) {
            return false;
        }
        return videoTypeMap.containsValue(fileType.toLowerCase());
    }

    /**
     * 判断是否为多媒体类型
     * null返回fals
     *
     * @param fileType 检测文件类型
     * @return boolean 是多媒体类型返回true
     */
    public static boolean isMultimedia(String fileType) {
        if (fileType == null) {
            return false;
        }

        if (isVideo(fileType)) {
            return true;
        }

        if (isAudio(fileType)) {
            return true;
        }

        return isImg(fileType.toLowerCase());
    }

    public static String getType(InputStream in, String fileOriginalName) {
        String typeName = FileTypeUtil.getType(in);
        if (null == typeName) {
            // 未成功识别类型，扩展名辅助识别
            typeName = FileUtil.extName(fileOriginalName);
        } else if ("xls".equals(typeName)) {
            // xls、doc、msi的头一样，使用扩展名辅助判断
            final String extName = FileUtil.extName(fileOriginalName);
            if ("doc".equalsIgnoreCase(extName)) {
                typeName = "doc";
            } else if ("msi".equalsIgnoreCase(extName)) {
                typeName = "msi";
            }
        } else if ("zip".equals(typeName)) {
            // zip可能为docx、xlsx、pptx、jar、war等格式，扩展名辅助判断
            final String extName = FileUtil.extName(fileOriginalName);
            if ("docx".equalsIgnoreCase(extName)) {
                typeName = "docx";
            } else if ("xlsx".equalsIgnoreCase(extName)) {
                typeName = "xlsx";
            } else if ("pptx".equalsIgnoreCase(extName)) {
                typeName = "pptx";
            } else if ("jar".equalsIgnoreCase(extName)) {
                typeName = "jar";
            } else if ("war".equalsIgnoreCase(extName)) {
                typeName = "war";
            }
        }
        return typeName;
    }

    /**
     * 判断文件是否在允许名单内上传
     *
     * @author Doumou
     * @date 2020/9/21
     */
    public static boolean isPermit(String fileType) {
        if (null == fileType) {
            return false;
        }
        return permitTypeMap.containsValue(fileType.toLowerCase());
    }


    public static void main(String[] args) throws IOException {
        String text = "测试";
        InputStream inputStream = new ByteArrayInputStream(text.getBytes());
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = inputStream.read(buffer)) > -1) {
            outputStream.write(buffer, 0, len);
        }
        outputStream.flush();
        ByteArrayInputStream inputStream1 = new ByteArrayInputStream(outputStream.toByteArray());
        ByteArrayInputStream inputStream2 = new ByteArrayInputStream(outputStream.toByteArray());
        int read1 = inputStream1.read(buffer);
        int read2 = inputStream2.read(buffer);
        System.out.println("读取：" + read1 + "个字节");
        System.out.println("读取：" + read2 + "个字节");
    }


    @SneakyThrows
    public static String upload(String path, InputStream file) {
        //File为空
        if (Objects.isNull(file) || Objects.isNull(path)) {
            return null;
        }
        File filePath = new File(path);
        BufferedOutputStream bos;
        //判断文件是否存在
        if (!filePath.exists()) {
            File newFile = new File(filePath.getParent());
            newFile.mkdirs();
        }

        bos = new BufferedOutputStream(new FileOutputStream(filePath));

        int len;
        //缓冲
        byte[] bytes = new byte[1024];


        while ((len = file.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }

        bos.close();
        file.close();

        return path;
    }

    /**
     * 获取文件MIME类型
     *
     * @param filename
     * @return 类型
     */
    public static String getMimeType(String filename) {
        String type = null;
        Path path = Paths.get(filename);
        try {
            type = Files.probeContentType(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return type;
    }

}
